{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/tomasonjo/blogs/blob/master/llm/langchain_neo4j.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "lqp9wLWW-UFE",
    "outputId": "156a39ce-bcaf-4f78-d07e-62a343da4f81"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: neo4j in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (5.16.0)\n",
      "Requirement already satisfied: openai in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (1.6.1)\n",
      "Requirement already satisfied: langchain in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (0.1.0)\n",
      "Requirement already satisfied: langchain_openai in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (0.0.2.post1)\n",
      "Requirement already satisfied: pytz in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from neo4j) (2023.3.post1)\n",
      "Requirement already satisfied: anyio<5,>=3.5.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (3.5.0)\n",
      "Requirement already satisfied: distro<2,>=1.7.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (1.8.0)\n",
      "Requirement already satisfied: httpx<1,>=0.23.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (0.26.0)\n",
      "Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (1.10.12)\n",
      "Requirement already satisfied: sniffio in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (1.2.0)\n",
      "Requirement already satisfied: tqdm>4 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (4.65.0)\n",
      "Requirement already satisfied: typing-extensions<5,>=4.7 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from openai) (4.9.0)\n",
      "Requirement already satisfied: PyYAML>=5.3 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (6.0.1)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (2.0.21)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (3.9.0)\n",
      "Requirement already satisfied: dataclasses-json<0.7,>=0.5.7 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (0.6.3)\n",
      "Requirement already satisfied: jsonpatch<2.0,>=1.33 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (1.33)\n",
      "Requirement already satisfied: langchain-community<0.1,>=0.0.9 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (0.0.9)\n",
      "Requirement already satisfied: langchain-core<0.2,>=0.1.7 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (0.1.7)\n",
      "Requirement already satisfied: langsmith<0.1.0,>=0.0.77 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (0.0.77)\n",
      "Requirement already satisfied: numpy<2,>=1 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (1.26.2)\n",
      "Requirement already satisfied: requests<3,>=2 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (2.31.0)\n",
      "Requirement already satisfied: tenacity<9.0.0,>=8.1.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain) (8.2.2)\n",
      "Requirement already satisfied: tiktoken<0.6.0,>=0.5.2 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain_openai) (0.5.2)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.1.0)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.4)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.3)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.4.0)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.2.0)\n",
      "Requirement already satisfied: idna>=2.8 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from anyio<5,>=3.5.0->openai) (3.4)\n",
      "Requirement already satisfied: marshmallow<4.0.0,>=3.18.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (3.20.1)\n",
      "Requirement already satisfied: typing-inspect<1,>=0.4.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (0.9.0)\n",
      "Requirement already satisfied: certifi in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai) (2023.11.17)\n",
      "Requirement already satisfied: httpcore==1.* in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai) (1.0.2)\n",
      "Requirement already satisfied: h11<0.15,>=0.13 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n",
      "Requirement already satisfied: jsonpointer>=1.9 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from jsonpatch<2.0,>=1.33->langchain) (2.1)\n",
      "Requirement already satisfied: packaging<24.0,>=23.2 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from langchain-core<0.2,>=0.1.7->langchain) (23.2)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from requests<3,>=2->langchain) (2.0.4)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from requests<3,>=2->langchain) (2.1.0)\n",
      "Requirement already satisfied: regex>=2022.1.18 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from tiktoken<0.6.0,>=0.5.2->langchain_openai) (2023.10.3)\n",
      "Requirement already satisfied: mypy-extensions>=0.3.0 in /Users/tomazbratanic/anaconda3/lib/python3.11/site-packages (from typing-inspect<1,>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain) (1.0.0)\n"
     ]
    }
   ],
   "source": [
    "!pip install neo4j openai langchain langchain_openai"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2wg51pF7LVVi"
   },
   "source": [
    "# LangChain has added Cypher Search\n",
    "## With the LangChain library, you can conveniently generate Cypher queries, enabling an efficient retrieval of information from Neo4j.\n",
    "\n",
    "If you have developed or plan to implement any solution that uses Large Language Models, you have most likely heard of the LangChain library. LangChain library is the most widely known Python library used to develop applications that use LLMs in one or another capabilities. It is designed to be modular, allowing us to use any LLM in any available modules, such as chains, tools, memory, or agents.\n",
    "\n",
    "A month ago, I spent a week researching and implementing a solution allowing anyone to retrieve information from Neo4j directly from the LangChain library and use it in their LLM applications. I learned quite a lot about the internals of LangChain library and wrote up my experience in a blog post.\n",
    "\n",
    "A colleague of mine showed me a LangChain feature request, where the user requested that my work of having the option to retrieve information from the Neo4j database would be added as a module directly to the LangChain library so that no additional code or external modules would be needed to integrate Neo4j into LangChain applications. Since I was already familiar with LangChain internals, I decided to try and implement Cypher searching capabilities myself. I spent a weekend researching and coding the solution and ensuring it would conform to the contribution standards for it to be added to the library. Luckily, the maintainers of LangChain are very responsive and open to new ideas, and the Cypher Search has been added in the latest release of the LangChain library. Thanks to Harrison Chase for maintaining such a great library and also being very responsive to new ideas.\n",
    "\n",
    "In tutorial, I will show you how you can use the newly added Cypher Search in the LangChain library to retrieve information from a Neo4j database.\n",
    "\n",
    "## What is a knowledge graph\n",
    "LangChain has already integrations with Vector and SQL databases, so why do we need an integration with a Graph Database like Neo4j?\n",
    "Knowledge graphs are ideal for storing heterogeneous and highly connected data. For instance, the above image contains information about people, organizations, movies, websites, etc. While the ability to intuitively model and store diverse sets of data is incredible, I think the main benefit of using graphs is the ability to analyze data points through their relationships. Graphs enable us to uncover connections and correlations that might otherwise remain unnoticed with traditional database and analytics approaches as they often overlook the context surrounding individual data points.\n",
    "\n",
    "The power of graph databases truly shines when dealing with complex systems where interdependencies and interactions are vital in understanding the system.\n",
    "They enable us to see beyond individual data points and delve into the intricate relationships that define their context. This provides a deeper, more holistic view of the data, facilitating better decision-making and knowledge discovery.\n",
    "\n",
    "## Setting up Neo4j environment\n",
    "\n",
    "If you have an existing Neo4j database, you can use it to try the newly added Cypher Search. The Cypher Search module uses graph schema information to generate Cypher statements, meaning you can plug it into any Neo4j database.\n",
    "If you don't have any Neo4j database yet, you can use [Neo4j Sandbox](https://neo4j.com/sandbox/), which offers a free cloud instance of a Neo4j database. You need to register and instantiate any of the available pre-populated databases. I will be using the [ICIJ Paradise Papers dataset](https://sandbox.neo4j.com/?usecase=icij-paradise-papers) in this blog post, but you can use any other if you want. The dataset has been made available by International Consortium of Investigative Journalists as part of their Offshore Leaks Database.\n",
    "\n",
    "The graph contains four types of nodes:\n",
    "* Entity - The offshore legal entity. This could be a company, trust, foundation, or other legal entity created in a low-tax jurisdiction.\n",
    "* Officer - A person or company who plays a role in an offshore entity, such as beneficiary, director, or shareholder. The relationships shown in the diagram are just a sample of all the existing ones.\n",
    "* Intermediary - A go-between for someone seeking an offshore corporation and an offshore service provider - usually a law-firm or a middleman that asks an offshore service provider to create an offshore firm.\n",
    "* Address - The registered address as it appears in the original databases obtained by ICIJ.\n",
    "\n",
    "## Knowledge Graph Cypher Search\n",
    "The name Cypher Search comes from Cypher, which is a query language used to interact with graph databases like Neo4j.\n",
    "\n",
    "![langchain.drawio.png]()\n",
    "\n",
    "In order to allow LangChain to retrieve information from graph databases, I implemented a module that can convert the natural language to a Cypher statement, use it to retrieve data from Neo4j and return the retrieved information to the user in a natural language form. This two-way conversion process between natural language and database language not only enhances the overall accessibility of data retrieval but also greatly improves the user experience.\n",
    "\n",
    "\n",
    "The beauty of the LangChain library is in its simplicity. We only need a couple of lines of code and we can retrieve information from Neo4j using natural language."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "hTz9REHt-XxO"
   },
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.chains import GraphCypherQAChain\n",
    "from langchain_community.graphs import Neo4jGraph\n",
    "\n",
    "graph = Neo4jGraph(\n",
    "    url=\"bolt://44.202.7.166:7687\", \n",
    "    username=\"neo4j\", \n",
    "    password=\"possibilities-overlay-eddies\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "id": "DkEz1NTBFrXu"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.environ['OPENAI_API_KEY'] = \"sk-\"\n",
    "\n",
    "chain = GraphCypherQAChain.from_llm(\n",
    "    ChatOpenAI(temperature=0), graph=graph, verbose=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "UW50UoMUMRSg"
   },
   "source": [
    "Here, we are using the gpt-3.5-turbo model from OpenAI to generate Cypher statements. The Cypher statements are generated based on the graph schema, which means that, in theory, you can plug the Cypher chain into any Neo4j instance, and it should be able to answer natural language answers. Unfortunately, I haven't yet tested other LLM providers in their ability to generate Cypher statements since I don't have access to any of them. Still, I would love to hear your evaluation of other LLMs generating Cypher statements if you will give it a go. Of course, if you want to break the dependency on LLM cloud providers, you can always [fine-tune an open-source LLM to generate Cypher statements](https://towardsdatascience.com/fine-tuning-an-llm-model-with-h2o-llm-studio-to-generate-cypher-statements-3f34822ad5).\n",
    "\n",
    "Let's start with a simple test."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 258
    },
    "id": "nblHLc6TJnLT",
    "outputId": "5d54fa86-d442-4361-f24e-2ec068de16e2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n",
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (i:Intermediary)-[:CONNECTED_TO]->(e:Entity)\n",
      "RETURN i.name, COUNT(e) AS numEntities\n",
      "ORDER BY numEntities DESC\n",
      "LIMIT 1\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'i.name': 'Group - SUN Capital Partner Group', 'numEntities': 115}]\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': '\\nWhich intermediary is connected to most entites?\\n',\n",
       " 'result': 'The intermediary connected to the most entities is the Group - SUN Capital Partner Group, with a total of 115 entities.'}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({'query': \"\"\"\n",
    "Which intermediary is connected to most entites?\n",
    "\"\"\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "QMHK94FyMYnV"
   },
   "source": [
    "We can observe the generated Cypher statement and the retrieved information from Neo4j used to form the answer. That is as easy a setup as it gets. Let's move on to the next example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 222
    },
    "id": "_ia3wgn7JDg3",
    "outputId": "d2fada74-3351-49a1-c2ad-ed4fbc9d2d8b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n",
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (o:Officer)-[:OFFICER_OF]->(e:Entity)\n",
      "WHERE e.name = 'ZZZ-MILI COMPANY LTD.'\n",
      "RETURN o.name\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'o.name': 'Whitson - Iva Marie'}, {'o.name': 'Whitson - Claud S.'}, {'o.name': 'Whitson - Claud S.'}, {'o.name': 'FINSBURY NOMINEES LTD'}, {'o.name': 'EURO NOMINEES LTD.'}, {'o.name': 'EURO SECURITIES LTD.'}, {'o.name': 'COMPANY DIRECTORS LTD.'}]\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': '\\nWho are the officers of ZZZ-MILI COMPANY LTD.?\\n',\n",
       " 'result': \"I'm sorry, but I don't have the information about the officers of ZZZ-MILI COMPANY LTD.\"}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({'query':\"\"\"\n",
    "Who are the officers of ZZZ-MILI COMPANY LTD.?\n",
    "\"\"\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "X04jYeaeMhZe"
   },
   "source": [
    "Since we are using a graph, let's construct a question that would utilize the power of graph databases."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "IvGS4_c1CHQg"
   },
   "outputs": [],
   "source": [
    "#chain_improved.run(\"\"\"\n",
    "#How are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited related?\n",
    "#\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "W1Cqo3KrMoU1"
   },
   "source": [
    "The generated Cypher statement looks fine at first glance. However, there is a problem as the Cypher statements used the variable-length path finding syntax and also treated relationships as undirected. As a result, this type of query is highly unoptimized and would explode in the number of rows.\n",
    "\n",
    "The nice thing about gpt-3.5-turbo is that it follows hints and instructions we drop in the input. For example, we can ask it to find only the shortest path."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 278
    },
    "id": "ZVQPdna0GaLP",
    "outputId": "2e44963e-652d-44ce-ba6a-f33a2ab5b95e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n",
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH path = shortestPath((e1:Entity {name: 'SOUTHWEST LAND DEVELOPMENT LTD.'})-[*]-(e2:Entity {name: 'Dragon Capital Markets Limited'}))\n",
      "RETURN path\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'path': [{'sourceID': 'Paradise Papers - Appleby', 'jurisdiction': 'KY', 'service_provider': 'Appleby', 'countries': 'Cayman Islands', 'jurisdiction_description': 'Cayman Islands', 'type': 'CE', 'valid_until': 'Appleby data is current through 2014', 'ibcRUC': '50843', 'labels(n)': '[\"Entity\"]', 'name': 'SOUTHWEST LAND DEVELOPMENT LTD.', 'country_codes': 'CYM', 'incorporation_date': '1993-Oct-05', 'node_id': '82011899'}, 'REGISTERED_ADDRESS', {'sourceID': 'Paradise Papers - Appleby', 'valid_until': 'Appleby data is current through 2014', 'address': 'Clifton House', 'labels(n)': '[\"Address\"]', 'name': 'Clifton House; 75 Fort Street; Grand Cayman KY1-1108; Cayman Islands', 'country_codes': 'CYM', 'countries': 'Cayman Islands', 'node_id': '81027146'}, 'REGISTERED_ADDRESS', {'sourceID': 'Paradise Papers - Appleby', 'jurisdiction': 'KY', 'service_provider': 'Appleby', 'countries': 'British Virgin Islands;Cayman Islands', 'jurisdiction_description': 'Cayman Islands', 'type': 'CE', 'valid_until': 'Appleby data is current through 2014', 'ibcRUC': '251645', 'labels(n)': '[\"Entity\"]', 'name': 'Dragon Capital Markets Limited', 'country_codes': 'VGB;CYM', 'incorporation_date': '1996-May-02', 'node_id': '82014099'}]}]\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': '\\nHow are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited connected?\\nFind a shortest path.\\n',\n",
       " 'result': 'The entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited are connected through a shortest path.'}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({'query':\"\"\"\n",
    "How are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited connected?\n",
    "Find a shortest path.\n",
    "\"\"\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dKYeXyqnMrJm"
   },
   "source": [
    "Now that we dropped a hint that only the shortest path should be retrieved, we don't run into cardinality explosion troubles anymore. However, one thing I noticed is that the LLM sometimes doesn't provide the best results if a path object is returned. However, we can also fix that by instructing the model what information to use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 239
    },
    "id": "jpUGmHgzIF4i",
    "outputId": "dfb66d71-7eca-4595-cc69-2fab4d033b62"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n",
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH path = shortestPath((e1:Entity {name: 'SOUTHWEST LAND DEVELOPMENT LTD.'})-[*]-(e2:Entity {name: 'Dragon Capital Markets Limited'}))\n",
      "RETURN [node in nodes(path) | node.name] AS names, [rel in relationships(path) | type(rel)] AS relationships\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'names': ['SOUTHWEST LAND DEVELOPMENT LTD.', 'Clifton House; 75 Fort Street; Grand Cayman KY1-1108; Cayman Islands', 'Dragon Capital Markets Limited'], 'relationships': ['REGISTERED_ADDRESS', 'REGISTERED_ADDRESS']}]\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': '\\nHow are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited connected?\\nFind a shortest path.\\nReturn only name properties of nodes and relationship types\\n',\n",
       " 'result': 'The entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited are connected through the relationship types REGISTERED_ADDRESS and REGISTERED_ADDRESS.'}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({'query':\"\"\"\n",
    "How are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited connected?\n",
    "Find a shortest path.\n",
    "Return only name properties of nodes and relationship types\n",
    "\"\"\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "i1ISIYQvMxEF"
   },
   "source": [
    "Now we can a better response and more appropriate response. The more hints you drop to an LLM, the better results you can expect. For example, you can also instruct it which relationships it can traverse."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 278
    },
    "id": "Sxhj6n7NT6bd",
    "outputId": "6b431f95-986e-41bf-d13e-f5da5e2b8694"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n",
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH path = shortestPath((e1:Entity {name: 'SOUTHWEST LAND DEVELOPMENT LTD.'})-[:OFFICER_OF|INTERMEDIARY_OF|CONNECTED_TO*]-(e2:Entity {name: 'Dragon Capital Markets Limited'}))\n",
      "RETURN [node in nodes(path) | node.name] AS names, [rel in relationships(path) | type(rel)] AS relationships\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'names': ['SOUTHWEST LAND DEVELOPMENT LTD.', 'Appleby Trust (Cayman) Ltd.', 'Dragon Capital Clean Development Investments Ltd.', 'Group - Dragon Capital', 'Dragon Capital Markets Limited'], 'relationships': ['INTERMEDIARY_OF', 'INTERMEDIARY_OF', 'CONNECTED_TO', 'CONNECTED_TO']}]\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomazbratanic/anaconda3/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:189: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': '\\nHow are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited connected?\\nFind a shortest path and use only officer, intermediary, and connected relationships.\\nReturn only name properties of nodes and relationship types\\n',\n",
       " 'result': 'The entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited are connected through intermediary and connected relationships. The shortest path between them is as follows:\\n\\nSOUTHWEST LAND DEVELOPMENT LTD. (INTERMEDIARY_OF) Appleby Trust (Cayman) Ltd. (CONNECTED_TO) Group - Dragon Capital (CONNECTED_TO) Dragon Capital Markets Limited'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({'query':\"\"\"\n",
    "How are entities SOUTHWEST LAND DEVELOPMENT LTD. and Dragon Capital Markets Limited connected?\n",
    "Find a shortest path and use only officer, intermediary, and connected relationships.\n",
    "Return only name properties of nodes and relationship types\n",
    "\"\"\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "myeTTU1zM0b-"
   },
   "source": [
    "## Summary\n",
    "Graph databases are an excellent tool for retrieving or analyzing the connections between various entities like people and organizations. In this blog post, we looked at a simple shortest path use case, where the number of relationships and the sequence of relationship types is unknown beforehand. These types of queries are virtually impossible in a vector database and could also be quite complicated in a SQL database.\n",
    "I am thrilled about the addition of Cypher Search to the LangChain library. Please test it out, and let me know how it works for you, especially if you are testing it on other LLM models or have exciting use cases."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "5mmBVoJiIr9K"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "authorship_tag": "ABX9TyOcXBcR86rPwNdXV2pzMJOf",
   "include_colab_link": true,
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
